# -*- coding: utf-8 -*-
'''
@File    :   ping_ip.py
@Time    :   2022/03/20 18:30:25

'''

import subprocess
import time
from typing import Union, List

from PyQt5.QtCore import QObject, QThread, pyqtSignal
from log import logger
from ping3 import ping

from project import IpState


class WrapPing:
    
    @staticmethod
    def ping(*args, **kwargs):
        try:
            code = ping(*args, **kwargs)
        except:
            code = False
        return code
        

class WrapPopenPing:
    @staticmethod
    def ping(*args, **kwargs):
        ip = kwargs.get('ip', None )
        if ip is None:
            ip = args[0]
        p = subprocess.Popen(['ping.exe', ip], stdin=subprocess.PIPE, stdout=subprocess.PIPE,
                             shell=False)
        res = p.stdout.readlines()
        for line in res:
            if 'TTL' in line.decode('gbk'):
                return True
        else:
            return False
    

class PingBase(QObject):
    signal_ping_send_ip = pyqtSignal(str)

    def __init__(self, ip, timesleep = 0.8, enable_stop = True):
        super(PingBase, self).__init__()
        if isinstance(ip, IpState):
            self._ip = ip.ip
            self.ipo = ip
        else:
            self.ipo = IpState(ip)
            self._ip = ip
        
        self.timesleep = timesleep
        self.enable_stop = enable_stop
    
    @property
    def ip(self):
        return self._ip
    
    def set_stop(self, enable=True):
        self.enable_stop = enable
    
    def run(self):
        while True:
            if self.get_code():
                self.signal_ping_send_ip.emit(self.ip)
                self.ipo.set_new()
            else:
                # self.signal_ping_check_end.emit()
                self.signal_ping_send_ip.emit("no" + self.ip)
                self.ipo.set_unknow()
                
            if self.enable_stop:
                break
            else:
                time.sleep(self.timesleep)
            
    def get_code(self):
        pass
    
    def create_thread(self):
        th = QThread()
        self.moveToThread(th)
        th.started.connect(self.run)
        return th


class PingIp(PingBase):
    """
    # 不用了,速度太慢了
    
    """
    def get_code(self):
        return WrapPopenPing.ping(self.ip)
        

class PingIp3(PingBase):
    """
    ping IP
    :argument ip  : str

    """
    def get_code(self):
        return WrapPing.ping(self.ip, timeout=2)


class PingIp3While(PingBase):
    # signal_pingip_while = pyqtSignal()
    def __init__(self, ip, timesleep = 0.8, enable_stop = False):
        super(PingIp3While, self).__init__(ip, timesleep, enable_stop)

    def get_code(self):
        return WrapPing.ping(self.ip, timeout=2)
    
    def run(self):
        
        while True:
            if self.ipo.state != IpState.STATE_INVALID:
                if self.get_code():
                    self.signal_ping_send_ip.emit(self.ip)
                    self.ipo.set_new()
                else:
                    # self.signal_ping_check_end.emit()
                    self.signal_ping_send_ip.emit("no" + self.ip)
                    self.ipo.set_unknow()
                
            if self.enable_stop:
                break
            else:
                time.sleep(self.timesleep)
    
            
            
            
class MthreadsBase(QObject):
    """
    todo 临时写的一个类
    
    """
    manage_signal_finishedall = pyqtSignal()  # 所有线程结束时发送
    manage_signal_send_onlineip = pyqtSignal(str)  # ping 通IP时发送
    manage_signal_oneth_end = pyqtSignal(int)  # 当一个线程结束时产生, 发送当前已经结束的线程数和总的线程数
    
    def __init__(self):
        super(MthreadsBase, self).__init__()
        self.ips = []
        self.ping_objs = []
        self.ths = []
        
    def quit(self):
        for th in self.ths:
            th.quit()
            th.wait()
        self.ths.clear()
        
        
    def start(self):
        for th in self.ths:
            th.start()

    def remove_ping_obj(self, ipstr):
        """
       从ping_objs列表中删除 ip 等于 ipstr 的 对象
        
        """
        for ping in self.ping_objs:
            if ping.ip == ipstr:
                self.ping_objs.remove(ping)
                return  True
        return False

    def set_ips(self, section: str):
        et = "192.168.{}.2".format(section)
        ipo = IpState(et)
        ips = list(ipo.generate_2_254_ips())
        self.ips = ips
        
        # f = lambda ip: logger.debug(ip)
        # list(map(f, self.ips))

    def __len__(self):
        return len(self.ths)


class ManageTheads(MthreadsBase):
    def create_threads(self):
        if not self.ips:
            return logger.warn("ips is blank")
    
        for ip in self.ips:
            self.create_one_thread(ip)
            
        logger.info("创建的线程数量为:  {}".format(len(self.ths)))
        
    def is_ping_objs_blank(self):
        return  len(self.ping_objs) == 0

    def create_one_thread(self, ipstr):
        ping_obj = PingIp3(ipstr)
        ping_obj.signal_ping_send_ip[str].connect(self.slot_send_ip)
        self.ths.append(ping_obj.create_thread())
        self.ping_objs.append(ping_obj)
            
    def slot_send_ip(self, ip_some: str):
        """
        当ip在线时,所发信号的糟函数
        当ip为"" 时,说明没有检测到;
        """
        if 'no' not in ip_some:
            self.manage_signal_send_onlineip.emit(ip_some)
        else:
            ip_some = ip_some.replace('no', '')
        
        self.remove_ping_obj(ip_some)
        
        i_send_value = 1 - len(self.ping_objs) / len(self.ths)
        i_send_value = 100 * i_send_value
        i_send_value = int(i_send_value)
        self.manage_signal_oneth_end.emit(i_send_value)
        
        if self.is_ping_objs_blank():
            self.manage_signal_finishedall.emit()


       
class ManagerProxyThread(QObject):
    
    def __init__(self):
        super(ManagerProxyThread, self).__init__()
        self.ipdict = {
            # key: ipstr
            # value : [th, pingobj]
        }
        pass
    
    def create_one(self, ipo: IpState):
        ipstr = ipo.ip
        ping = PingIp3While(ipo)
        th = ping.create_thread()
        self.ipdict.setdefault(ipstr, [th, ping])
        return th
    
    def _conver_str(self, ip):
        if isinstance(ip, str):
            return ip
        elif isinstance(ip, IpState):
            return ip.ip
        else:
            raise TypeError("ip type is error")
    
    def get_th(self, ipo) -> QThread:
        ipstr = self._conver_str(ipo)
        return self.ipdict.get(ipstr, [None])[0]
        
    def get_ping(self, ipo) -> PingIp3While:
        ipstr = self._conver_str(ipo)
        return self.ipdict.get(ipstr, [None, None])[1]
    
    def ths(self):
        return [arr[0] for arr in self.ipdict.values()]
    
    def pings(self):
        return [arr[1] for arr in self.ipdict.values()]
    
    def create_many_ths(self, l_ipo: list):
        for ipo in l_ipo:
            self.create_one(ipo)
            
    
    def start(self):
        for th in self.ths():
            th.start()
            
    
    def quit(self):
        for ping in self.pings():
            ping.set_stop()
            
        for th in self.ths():
            th.quit()
            th.wait()
        
        self.ipdict.clear()
        
    def runing_count(self):
        return len([th for th in self.ths() if th.isRunning()])
    
    def pop(self, ipo: IpState):
        th = self.get_th(ipo)
        ping = self.get_ping(ipo)
        if ping:
            ping.set_stop()
            
        if th: th.quit()
        self.ipdict.pop(ipo.ip, None)
    
        
    
    
        
    













if __name__ == '__main__':
    pass
